const Terminal = require('xterm').Terminal
const FitAddon = require('xterm-addon-fit').FitAddon
const Client = require('ssh2').Client
const fs = require('fs')
const vuedraggable = require('vuedraggable')
const Sortable = require('sortablejs')

let config_file = 'config.json'
let config = {}
if (fs.existsSync(config_file)) {
    var data = fs.readFileSync(config_file)
    config = JSON.parse(data)
}
config.sessions = config.sessions || []

var sessions = []
for (var i = 0; i < config.sessions.length; ++i) {
    sessions.push({
        id: i,
        name: config.sessions[i].name,
        host: config.sessions[i].host,
        port: config.sessions[i].port,
        username: config.sessions[i].username,
        password: config.sessions[i].password,
        privatekey: config.sessions[i].privatekey,
    })
}

new Vue({
    el: '#app',
    components: {
        vuedraggable
    },
    data() {
        return {
            visible: false,
            next_session_id: sessions.length,
            sessions: sessions,
            next_terminal_id: 0,
            terminal_tabs: [],
            terminals: [],
            activate_terminal: '',

            passphrase_dialog_visible: false,
            passphrase_dialog_label_width: '120px',
            passphrase_dialog_passphrase: '',
            passphrase_dialog_title: '',
            passphrase_dialog_errors: [],
            passphrase_dialog_confirm_func: null,
            passphrase_cache: {},

            add_session_dialog_visible: false,
            add_session_dialog_label_width: '120px',
            add_session_dialog_name: '',
            add_session_dialog_host: '',
            add_session_dialog_port: '',
            add_session_dialog_username: '',
            add_session_dialog_password: '',
            add_session_dialog_privatekey: '',
            add_session_dialog_errors: [],
            add_session_dialog_confirm_func: null,

            error_dialog_visible: false,
            error_dialog_title: '',
            error_dialog_errors: [],

            session_menu_options: [{
                name: 'Edit',
                slug: 'edit'
            }, {
                  name: '<em>Delete</em>',
                  slug: 'delete'
                }
            ],
        }
    },
    mounted() {
        window.addEventListener('resize', this.on_window_resize)
        this.make_terminal_draggable()
    },
    destroyed() {
        window.removeEventListener('resize', this.on_window_resize)
    },
    methods: {
        open_session(idx, passphrase, terminal_id) {
            passphrase = passphrase || ''
            let terminal_name = this.sessions[idx].name || (this.sessions[idx].host + ':' + this.sessions[idx].port + '(' + this.sessions[idx].name + ')')
            
            var privatekey = this.sessions[idx].privatekey
            if (privatekey && !passphrase) {
                if (privatekey in this.passphrase_cache) {
                    passphrase = this.passphrase_cache[privatekey]
                } else {
                    this.show_passphrase_dialog({
                        title: 'passphrase for ' + terminal_name,
                        on_confirm() {
                            this.open_session(idx, this.passphrase_dialog_passphrase)
                        }
                    })
                    return
                }
            }

            try {
                if (!terminal_id) {
                    terminal_id = this.next_terminal_id + ''
                    ++this.next_terminal_id
                }

                console.log('open session[' + idx + ',' + terminal_name + ',' + terminal_id + ']')

                var conn = new Client()
                conn.on('ready', () => {
                    console.log('Client[' + idx + ',' + terminal_name + ',' + terminal_id + '] ready')
                    conn.shell((err, stream) => {
                        if (err) {
                            console.log(err)
                            return
                        }

                        if (privatekey) {
                            this.passphrase_cache[privatekey] = passphrase
                        }

                        var need_new_terminal_tab = true
                        for (var i = 0; i < this.terminal_tabs.length; ++i) {
                            if (this.terminal_tabs[i].id == terminal_id) {
                                need_new_terminal_tab = false
                                break
                            }
                        }
                        if (need_new_terminal_tab) {
                            this.terminal_tabs.push({
                                id: terminal_id,
                                name: terminal_name,
                            })
                        }

                        var need_new_terminal = true
                        for (var i = 0; i < this.terminals.length; ++i) {
                            if (this.terminals[i].client == null || this.terminals[i].id == terminal_id) {
                                this.terminals[i] = {
                                    id: terminal_id,
                                    stream: stream,
                                    name: terminal_name,
                                    client: conn,
                                    has_error: false,
                                }

                                need_new_terminal = false
                                break
                            }
                        }

                        if (need_new_terminal) {
                            this.terminals.push({
                                id: terminal_id,
                                stream: stream,
                                name: terminal_name,
                                client: conn,
                                has_error: false,
                            })
                        }
                        
                        this.activate_terminal = terminal_id

                        Vue.nextTick(() => {
                            this.create_terminal(terminal_id)
                        })
                    })
                }).on('error', err => {
                    console.log('Client[' + idx + ',' + terminal_name + ',' + terminal_id + '] error.', err)
                    for (var i = 0; i < this.terminals.length; ++i) {
                        if (this.terminals[i].id == terminal_id) {
                            if (this.terminals[i].client) {
                                console.log('reopen session[' + idx + ',' + terminal_name + ',' + terminal_id + ']')
                                Vue.nextTick(() => {
                                    this.open_session(idx, passphrase, terminal_id)
                                })
                            } else {
                                console.log('close terminal[' + idx + ',' + terminal_name + ',' + terminal_id + ']')
                                this.close_terminal(terminal_id)
                            }

                            this.terminals[i].has_error = true
                            return
                        }
                    }
                    
                    if (this.sessions[idx].privatekey) {
                        Vue.nextTick(() => {
                            this.show_passphrase_dialog({
                                title: 'passphrase for ' + terminal_name,
                                errors: [err.message],
                                on_confirm() {
                                    this.open_session(idx, passphrase, terminal_id)
                                }
                            })
                        })
                    } else {
                        Vue.nextTick(() => {
                            this.show_error_dialog({
                                title: 'error from ' + terminal_name,
                                errors: [err.message]
                            })
                        })
                    }
                }).connect({
                    host: this.sessions[idx].host,
                    port: this.sessions[idx].port,
                    username: this.sessions[idx].username,
                    password: this.sessions[idx].password,
                    privateKey: this.sessions[idx].privatekey,
                    passphrase: passphrase,
                })
            } catch (err) {
                console.log(err)
                Vue.nextTick(() => {
                    this.show_passphrase_dialog({
                        title: 'passphrase for ' + terminal_name,
                        errors: [err.message],
                        on_confirm() {
                            this.open_session(idx, this.passphrase_dialog_passphrase, terminal_id)
                        }
                    })
                })
            }
        },
        create_terminal(id) {
            console.log('create terminal[' + id + ']')
            var dom = document.getElementById('terminal-' + id)
            if (!dom) {
                Vue.nextTick(() => {
                    this.create_terminal(id)
                })
                return
            }

            dom.innerHTML = ''
            for (var i = 0; i < this.terminals.length; ++i) {
                if (this.terminals[i].id == id) {
                    var term = new Terminal()
                    term.open(dom)
                    this.terminals[i].terminal = term
                    
                    fitAddon = new FitAddon()
                    term.loadAddon(fitAddon)
                    fitAddon.fit()
                    this.terminals[i].fitAddon = fitAddon

                    var stream = this.terminals[i].stream
                    stream.on('close', () => {
                        for (var i = 0; i < this.terminals.length; ++i) {
                            if (this.terminals[i].id == id) {
                                console.log('Stream[' + this.terminals[i].name +  ',' + id + '] close')
                                if (this.terminals[i].client) {
                                    this.terminals[i].client.end()
                                    this.terminals[i].stream.end()
                                    this.terminals[i].client = null
                                    this.terminals[i].stream = null
                                    this.terminals[i].terminal = null
                                    this.terminals[i].fitAddon = null
                                }

                                if (!this.terminals[i].has_error)
                                    this.close_terminal(id)

                                return
                            }
                        }
                    }).on('data', function(data) {
                        // console.log('OUTPUT: ' + data);
                        term.write(data)
                    })
                    // stream.end('ls -l\nexit\n');
                    term.onData(data => {
                        // console.log(data)
                        stream.write(data)
                    })
                    
                    stream.write("\nstty rows " + term.rows + " cols " + term.cols + "\n")
                    term.focus()
                    return
                }
            }
        },
        close_terminal(id) {
            console.log('close terminal[' + id + ']')

            if (id === this.activate_terminal) {
                this.terminal_tabs.forEach((elem, index) => {
                    if (elem.id === id) {
                        let nextTab = this.terminal_tabs[index + 1] || this.terminal_tabs[index - 1]
                        if (nextTab) {
                            this.activate_terminal = nextTab.id
                        }
                    }
                })
            }
            
            this.terminal_tabs = this.terminal_tabs.filter(elem => elem.id !== id)
            
            this.terminals.forEach((elem, index) => {
                if (elem.id === id) {
                    if (elem.client) {
                        elem.client.end()
                        elem.stream.end()
                        elem.client = null
                        elem.stream = null
                        elem.terminal = null
                        elem.fitAddon = null
                    }
                    
                    var dom = document.getElementById('terminal-' + id)
                    if (dom) {
                        dom.innerHTML = ''
                    }
                }
            })
            
            // this.terminals = this.terminals.filter(elem => elem.id !== id)

            var left_terminal_tabs = ''
            this.terminal_tabs.forEach((elem, index) => {
                left_terminal_tabs += elem.id + ' '
            })
            console.log('left terminal tabs: ' + left_terminal_tabs)
            
            var left_terminals = ''
            this.terminals.forEach((elem, index) => {
                left_terminals += elem.id + ' '
            })
            console.log('left terminals: ' + left_terminals)
        },
        on_window_resize() {
            for (var i = 0; i < this.terminals.length; ++i) {
                if (this.terminals[i].id == this.activate_terminal) {
                    console.log('on_window_resize[' + this.terminals[i].name + ',' + this.activate_terminal + ']')
                    if (this.terminals[i].terminal) {
                        let term = this.terminals[i].terminal
                        let rows = term.rows
                        let cols = term.cols
                        this.terminals[i].fitAddon.fit()
                        if (term.rows != rows || term.cols != cols) {
                            term.scrollToBottom()                            
                            this.terminals[i].stream.write("\nstty rows " + term.rows + " cols " + term.cols + "\n")
                        }
                        term.focus()
                    }
                    
                    return
                }
            }
        },
        show_passphrase_dialog(obj) {
            this.passphrase_dialog_passphrase = ''
            this.passphrase_dialog_title = obj.title || 'passphrase'
            this.passphrase_dialog_errors = obj.errors || []
            this.passphrase_dialog_visible = true
            this.passphrase_dialog_confirm_func = obj.on_confirm
            Vue.nextTick(() => {
                this.$refs.passphrase_dialog_passphrase.focus()
            })
        },
        on_passphrase_dialog_confirm() {
            this.passphrase_dialog_confirm_func()
            this.passphrase_dialog_visible = false
        },
        show_add_session_dialog(obj) {
            this.add_session_dialog_name = obj.name || ''
            this.add_session_dialog_host = obj.host || ''
            this.add_session_dialog_port = obj.port || ''
            this.add_session_dialog_username = obj.username || ''
            this.add_session_dialog_password = obj.password || ''
            this.add_session_dialog_privatekey = obj.privatekey || ''
            this.add_session_dialog_errors = obj.errors || []
            this.add_session_dialog_visible = true
            this.add_session_dialog_confirm_func = obj.on_confirm
        },
        on_add_session_dialog_confirm() {
            this.add_session_dialog_confirm_func()
            this.add_session_dialog_visible = false
        },
        add_session() {
            var on_confirm = () => {
                this.add_session_dialog_errors = []
                if (!this.add_session_dialog_host) {
                    this.add_session_dialog_errors.push('host不能为空')
                }
                
                if (!this.add_session_dialog_port) {
                    this.add_session_dialog_errors.push('port不能为空')
                }
                
                if (!this.add_session_dialog_username) {
                    this.add_session_dialog_errors.push('username不能为空')
                }
                
                if (!this.add_session_dialog_password && !this.add_session_dialog_privatekey) {
                    this.add_session_dialog_errors.push('password和privatekey不能同时为空')
                }
    
                if (this.add_session_dialog_errors.length != 0) {
                    Vue.nextTick(() => {
                        this.show_add_session_dialog({
                            name: this.add_session_dialog_name,
                            host: this.add_session_dialog_host,
                            port: this.add_session_dialog_port,
                            username: this.add_session_dialog_username,
                            password: this.add_session_dialog_password,
                            privatekey: this.add_session_dialog_privatekey,
                            errors: this.add_session_dialog_errors,
                            on_confirm
                        })
                    })
                    return
                }
    
                this.sessions.push({
                    id: this.next_session_id,
                    name: this.add_session_dialog_name,
                    host: this.add_session_dialog_host,
                    port: this.add_session_dialog_port,
                    username: this.add_session_dialog_username,
                    password: this.add_session_dialog_password,
                    privatekey: this.add_session_dialog_privatekey,
                })
                ++this.next_session_id
            }
            this.show_add_session_dialog({
                on_confirm,
            })
        },
        make_terminal_draggable() {
            const el = document.querySelector('.el-tabs__nav')
            if (!el) {
                console.log('no dom .el-tabs__nav')
                setTimeout(() => {
                    this.make_terminal_draggable()
                }, 1000)
                return
            }

            const _this = this
            Sortable.create(el, {
                onEnd({newIndex, oldIndex}) {                              //oldIIndex拖放前的位置， newIndex拖放后的位置
                    console.log('drag' + newIndex + ' to ' + oldIndex)
                    const currRow = _this.terminal_tabs.splice(oldIndex, 1)[0] //鼠标拖拽当前的el-tabs-pane
                    _this.terminal_tabs.splice(newIndex, 0, currRow)           //tableData 是存放所以el-tabs-pane的数组
                    console.log(_this.terminal_tabs[0].id + ' ' + _this.terminal_tabs[1].id)
            　　}
            })
        },
        show_session_menu(event, item) {
            this.$refs.session_menu.showMenu(event, item)
        },
        on_session_menu_click(event) {
            console.log(event)
            if (event.option.name == 'Edit') {
                var on_confirm = () => {
                    this.add_session_dialog_errors = []
                    if (!this.add_session_dialog_host) {
                        this.add_session_dialog_errors.push('host不能为空')
                    }
                    
                    if (!this.add_session_dialog_port) {
                        this.add_session_dialog_errors.push('port不能为空')
                    }
                    
                    if (!this.add_session_dialog_username) {
                        this.add_session_dialog_errors.push('username不能为空')
                    }
                    
                    if (!this.add_session_dialog_password && !this.add_session_dialog_privatekey) {
                        this.add_session_dialog_errors.push('password和privatekey不能同时为空')
                    }

                    if (this.add_session_dialog_errors.length != 0) {
                        Vue.nextTick(() => {
                            this.show_add_session_dialog({
                                name: this.add_session_dialog_name,
                                host: this.add_session_dialog_host,
                                port: this.add_session_dialog_port,
                                username: this.add_session_dialog_username,
                                password: this.add_session_dialog_password,
                                privatekey: this.add_session_dialog_privatekey,
                                errors: this.add_session_dialog_errors,
                                on_confirm
                            })
                        })
                        return
                    }

                    for (var i = 0; i < this.sessions.length; ++i) {
                        if (this.sessions[i].id == event.item.id) {
                            this.sessions[i].name = this.add_session_dialog_name
                            this.sessions[i].host = this.add_session_dialog_host
                            this.sessions[i].port = this.add_session_dialog_port
                            this.sessions[i].username = this.add_session_dialog_username
                            this.sessions[i].password = this.add_session_dialog_password
                            this.sessions[i].privatekey = this.add_session_dialog_privatekey
                            break
                        }
                    }
                }
                this.show_add_session_dialog({
                    name: event.item.name,
                    host: event.item.host,
                    port: event.item.port,
                    username: event.item.username,
                    password: event.item.password,
                    privatekey: event.item.privatekey,
                    on_confirm
                })
            } else if (event.option.name == 'Delete') {
                this.sessions = this.sessions.filter(elem => elem.id !== event.item.id)
            }
        },
        show_error_dialog(obj) {
            this.error_dialog_title = obj.title || '提示'
            this.error_dialog_errors = obj.errors || []
            this.error_dialog_visible = true
        },
    },
    watch: {
        sessions(val) {
            config.sessions = []
            for (var i = 0; i < val.length; ++i) {
                config.sessions.push({
                    name: val[i].name,
                    host: val[i].host,
                    port: val[i].port,
                    username: val[i].username,
                    password: val[i].password,
                    privatekey: val[i].privatekey,
                })
            }
            console.log(config.sessions)
            var data = JSON.stringify(config, null, 4)
            console.log(data)
            fs.writeFileSync(config_file, data)
        },
        activate_terminal(val) {
            Vue.nextTick(() => {
                this.on_window_resize()
            })
        }
    }
})
